package com.linkgie.galaxy.ai.acr.client;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.function.Function;

public class GalaxyUserAuthenticationGenerator {

    // 基于 HTTP 协议标准头部 Authorization 进行扩展的认证方案 GalaxyUser；
    public static final String HEADER = "Authorization";
    // 基于 Galaxy 平台的用户名密码的认证方案；
    public static final String AUTH_SCHEME = "GalaxyUser";

    public static final String HEADER_VALUE_PREFIX = AUTH_SCHEME + " ";

    /**
     * AUTHORIZATION 认证摘要分隔符；
     */
    public static final String AUTH_SECTION_SPERATOR = ";";

    /**
     * 信息字段分隔符；
     */
    public static final String AUTH_FIELD_SPERATOR = ":";

    /**
     * 以当前时间戳生成认证信息；
     * 
     * @param username 用户名；
     * @param password 密码；
     * @param appId    应用唯一标识；
     * @param secret   应用密钥；
     * @return
     */
    public static String generateAuthorization(String username, String password, String appId, String secret) {
        long timestamp = System.currentTimeMillis();
        return generateAuthorization(username, password, timestamp, appId, secret);
    }

    /**
     * 以指定时间戳生成认证信息；
     * 
     * @param username  用户名；
     * @param password  密码；
     * @param timestamp 时间戳；当发送请求时，需要与AI服务端的时间保持一致，偏差不能超过 10 秒；
     * @param appId     应用唯一标识；从控制台中以管理员登录进行查看；
     * @param secret    应用密钥；从控制台中以管理员登录进行查看；注意，不要泄露给第三方；
     * @return
     */
    public static String generateAuthorization(String username, String password, long timestamp, String appId,
            String secret) {

        // 生成用户名和密码的部分；
        String usernamePassword = String.join(AUTH_FIELD_SPERATOR,
                username, password);
        // 生成摘要；
        String digestPart = generateDigestPart(usernamePassword, timestamp, appId, secret,
                GalaxyUserAuthenticationGenerator::sha256);
        // 生成 token；
        String token = generateAuthToken(usernamePassword, timestamp, digestPart);
        // 加入前缀；
        String auth = HEADER_VALUE_PREFIX + token;

        return auth;
    }


    private static String generateAuthToken(String usernamePassword, long timestamp, String digestPart) {
        String usernamePasswordPart = Base64.getEncoder()
                .encodeToString(toBytes_UTF8(usernamePassword));

        String token = String.join(AUTH_SECTION_SPERATOR,
                usernamePasswordPart, timestamp + "", digestPart);
        return token;
    }

    /**
     * 生成摘要部分；
     *
     * @param usernamePassword
     * @param timestamp
     * @param appId
     * @param secret
     * @param digestFunction
     * @return
     */
    private static String generateDigestPart(String usernamePassword, long timestamp, String appId, String secret,
            Function<String, byte[]> digestFunction) {
        String message = String.join(AUTH_FIELD_SPERATOR,
                usernamePassword, timestamp + "", appId, secret);
        byte[] digestBytes = digestFunction.apply(message);
        String digestPart = Base64.getEncoder().encodeToString(digestBytes);
        return digestPart;
    }

    /**
     * 基于 JDK 的类库实现；
     * 
     * @param message
     * @return
     */
    private static byte[] sha256(String message) {
        // 获取SHA256算法的MessageDigest实例
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        // 使用update方法将字节数据传入进行摘要计算
        digest.update(toBytes_UTF8(message));
        // 通过digest方法获取最终的哈希摘要结果，返回的是字节数组
        byte[] hash = digest.digest();
        return hash;
    }

    private static byte[] toBytes_UTF8(String str) {
        if (null == str) {
            return null;
        }
        try {
            byte[] bytes = str.getBytes("UTF-8");
            return bytes;
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }
}
